home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1993 / Internet Info CD-ROM (Walnut Creek) (1993).iso / networking / ip / ka9q / kiss.mac < prev    next >
Encoding:
Text File  |  1990-07-29  |  56.7 KB  |  2,102 lines

  1. ;
  2. ;             KISS TNC for the TNC-2 and clones
  3. ;
  4. ; k3mc 30 Sep 86 - original version
  5. ;
  6. ; 1 Mar 87.  Fixed all known bugs.  Re-arrange code to allow ROMing (this
  7. ; means that data areas need to be initialized from the code).  Figure out the
  8. ; Stack Pointer given the amount of available RAM.  Include the codes 05 00
  9. ; and 05 01 to mean full duplex off and full duplex on, respectively.
  10. ; Clear out all available RAM.  Do a "dance" with LEDs when initially booted:
  11. ; Flash the LED(s) for about 5 seconds such that CON only flashes if you have
  12. ; 8k RAM, STA only flashes if 16k RAM, and STA and CON flash if 32k RAM.
  13. ;
  14. ; 29 Mar 87. Add code to discard BREAK chars, and chars with framing errors.
  15. ; Fix bug in ib_rca which did not discard null received frames.
  16. ;
  17. ; 11 Dec 89.  Incorporate code from Jan Schiefer, DL5UE, [44.130.48.9]
  18. ; Degerlocherstrasse 5, 7000 Stuttgart 70, Federal republic of Germany
  19. ; to fix the problem with Full-Duplex operation.  New version number, v.4
  20.  
  21.  
  22. FALSE    equ    0
  23. TRUE    equ    NOT FALSE
  24.  
  25.     .z80
  26.     aseg
  27.     org    100h        ;silly stuff for CP/M...
  28.  
  29.  
  30. ;ROM    equ    TRUE        ;uncomment this line to get ROM code 
  31.  
  32. ROM    equ    FALSE        ;uncomment this line for downloadable code
  33.  
  34.  
  35. ;Note: Next two equates don't matter unless ROM is True.
  36.  
  37. HOWIE    equ    FALSE        ;uncomment for ROM version org at 0
  38.  
  39. ;HOWIE    equ    TRUE        ;uncomment for org at 4800h for inclusion in
  40.                 ;Howie's code.
  41.  
  42.     if    ROM
  43.         if    HOWIE
  44.             .phase    4800h
  45. I_Register        equ    48h
  46.         else
  47.             .phase    0
  48. I_Register        equ    0
  49.         endif;    HOWIE
  50.     else
  51.             .phase    8000h
  52. I_Register        equ    80h
  53.     endif;    ROM
  54.  
  55.  
  56. SIO    equ    0dch        ;actually, only A5 is used for SIO -cs
  57.  
  58. A_dat    equ    SIO+0        ;Modem port
  59. A_ctl    equ    SIO+1        ;Modem port
  60.  
  61. B_dat    equ    SIO+2        ;user serial port
  62. B_ctl    equ    SIO+3        ;user serial port
  63.  
  64. DCD    equ    8        ;Bit in RR0, used in Ch A
  65.  
  66. TBE    equ    4        ;TX Buffer Empty bit
  67. RTS    equ    2        ;Request To Send (PTT bit in WR5 of Chan A)
  68. Framing_Error    equ    40h    ;Bit in RR1 for async framing error
  69. Break_Abort    equ    80h    ;Bit in RR0 for async Break detection
  70.  
  71. FEND    equ    300o        ;300 octal
  72. FESC    equ    333o        ;333 octal
  73. TFEND    equ    334o        ;334 octal
  74. TFESC    equ    335o        ;335 octal
  75.  
  76. ALEDon    equ    69h        ;bits for WR5 to turn on  STA LED
  77. ALEDoff    equ    0e9h        ;bits for WR5 to turn off STA LED
  78.  
  79. ALED    equ    80h        ;The DTR Bit in Ch A WR5, we will soon remove
  80.                 ;previous 2 definitions & use the memory loc.
  81.                 ;A_WR5 to hold Ch A WR5's value, because we
  82.                 ;need to be aware when we are transmitting!
  83.  
  84. BLEDon    equ    6ah        ;bits for WR5 to turn on  CON LED
  85. BLEDoff    equ    0eah        ;bits for WR5 to turn off CON LED
  86. BLED    equ    80h
  87.  
  88. N_events    equ    3    ;so far, only 3 real-time events
  89.                 ; 1 test event left untouched
  90.  
  91. start:
  92.     jp    code_start    ;go around this data area
  93. version:
  94.     defb    "v.4 11 Dec 89"    ;13 bytes (exactly!) here for version string
  95.  
  96.     defw    ib_tbe        ;ch B transmitter buffer empty interrupt/user
  97.     defw    ib_ext        ;ch B ext/status change/user
  98.     defw    ib_rca        ;ch B received char available/user
  99.     defw    ib_special    ;ch B special receive condition/user
  100.  
  101.     defw    ia_tbe        ;ch A transmitter buffer empty interrupt/modem
  102.     defw    ia_ext        ;ch A ext/status change/modem
  103.     defw    ia_rca        ;ch A received char available/modem
  104.     defw    ia_special    ;ch A special receive condition/modem
  105.  
  106. a_init:
  107.     defb    18h,4,20h,1,1bh,7,7eh,5,0e9h,3,0c9h    ;For Modem
  108. a_size    equ    $-a_init
  109.  
  110. b_init:
  111.     defb    18h,4,44h,2,10h,3,0c1h,5,0eah,1,1fh    ;For TTY
  112. b_size    equ    $-b_init
  113.  
  114.  
  115. ;This is the data area which gets blasted into RAM upon startup:
  116. data_init:
  117.  
  118. ;nbuffers:
  119.         db    0        ;up to 255 buffers
  120. ;free:
  121.         dw    0        ;address of 1st buffer on free list
  122.  
  123. ;RX_buf:
  124.         dw    0        ;address of current Receive buffer
  125. ;RX_head:
  126.         dw    0        ;address of 1st RX buffer
  127. ;RX_Allocated_Buffer:
  128.         db    0        ;set non-zero if we're in RX state
  129.  
  130. ;RX_Flushing:
  131.         db    0        ;is non-0 if we ran out of buffer
  132.                     ;space and are currently flushing this
  133.                     ;frame being received.  Used by
  134.                     ;ia_rca and reset by ia_ext.
  135.  
  136.  
  137. ;In_Buffer:
  138.         dw    0        ;addr of current Input buffer
  139. ;In_Head:
  140.         dw    0        ;addr of 1st Input Buffer
  141. ;In_Allocated_Buffer:
  142.         db    0        ;is not 0 if we've already alloc'd buf
  143.  
  144. ;In_State:
  145.         db    1        ;convert back to 1 in v.32 code
  146.  
  147.                     ;input state machine state
  148.                     ;4 Mar 8: Make it 0 (from 1) becuz
  149.                     ;noise on line is first triggering the
  150.                     ;code to assume that a frame is coming
  151.                     ;from the host.....  Comment below was
  152.                     ;appropriate before
  153.                     ;assume that we've seen an FEND from
  154.                     ;(non-existent) "previous" frame. This
  155.                     ;means that when we are receiving data
  156.                     ;from user, there need be ONLY the
  157.                     ;FEND char at the end of a frame, and
  158.                     ;not at the beginning (although if a
  159.                     ;FEND is at the beginning, it is 
  160.                     ;ignored.)
  161.  
  162. ;Out_Started:
  163.         db    0        ;Output not started yet (Logical var)
  164. ;Out_Head_CBuf:
  165.         dw    out_top        ;address of buffer to be output rs232
  166. ;Out_Tail_CBuf:
  167.         dw    out_top        ;pointer to next free output buffer
  168. ;Out_Chain_Head:
  169.         dw    0        ;addr of buffer we are now outputting
  170.  
  171. ;TX_Started:
  172.         db    0        ;non-zero if we've begun TXing chars
  173. ;TX_Head_CBuf:
  174.         dw    TX_Top        ;Current active CBuf entry (if active)
  175. ;TX_Tail_CBuf:
  176.         dw    TX_Top        ;next free CBuf entry
  177.  
  178. ;TX_Chain_Head:
  179.         dw    0        ;holds address of the current buffer
  180.                     ;chain head that we are transmitting
  181. ;TX_Outstanding:
  182.         db    0        ;Number of TX CBufs queued up for TX
  183.  
  184. ;DCD_State:
  185.         db    0        ;is non 0 if DCD LED is on
  186. ;S_H_State:
  187.         db    1        ;Means we are in Sync/Hunt state
  188.  
  189. ;these next two are used by the IB_TBE interrupt routine.
  190. ;ib_esc_mode:
  191.         db    0        ; not in escaped mode 
  192. ;ib_char:
  193.         ds    1        ; next char to send if escaped mode
  194. ;in_break:
  195.         db    0        ; non-zero if we are in a break detect
  196.                     ; sequence on the async port
  197. ;Full_Duplex:
  198.         db    0        ;not Full Duplex to start
  199. ;A_WR5:
  200.         db    ALEDoff        ;state of STA LED & RTS (PTT) line,
  201.                     ;mainly... (For Ch A only [modem] )
  202. ;B_WR5:
  203.         db    BLEDoff
  204.  
  205. data_size    equ    $-data_init
  206.  
  207. ;***************************************************************************
  208. code_start:
  209.     di                ;No interrupts for the moment...
  210.  
  211. ;Init SIO.  This is required even if we wanna flash LEDs...
  212.  
  213.     in    a,(A_ctl)        ;assure we are talking to ch 0
  214.     ld    c,A_ctl
  215.     ld    b,a_size
  216.     ld    hl,a_init
  217.     otir                ;init sync (modem) port
  218.  
  219. ;Init Async port, also to allow flashing LEDs
  220.  
  221.     in    a,(B_ctl)        ;assure we are talking to ch 0
  222.     ld    c,B_ctl
  223.     ld    b,b_size
  224.     ld    hl,b_init
  225.     otir                ;init async port & interrupt vector
  226.  
  227. ;
  228. ; Figure out where top of stack is, set stack pointer.
  229. ; silly TNC-2 does not do complete address decoding for the RAMs if you are
  230. ; using only the two 8k x 8 chips.  Hack to figure out top of memory so we can
  231. ; set stack pointer. Newer hack to see if we've only got 8k RAM.
  232.  
  233.     ld    a,(9fffh)    ;top of RAM if only 8K
  234.     cpl
  235.     ld    b,a
  236.     ld    (9fffh),a    ;write one's complement into mem
  237.  
  238.     ld    a,(9fffh)    
  239.     cp    b        ;see if it "took"
  240.     jp    z,ok_8        ;Yes, we have at least 8k of RAM
  241.  
  242.  
  243.     halt            ;else there is no RAM, so stop
  244.  
  245. ok_8:
  246.     ld    a,(0bfffh)    ;Top of RAM if 16K
  247.     cpl
  248.     ld    b,a
  249.     ld    (0bfffh),a    ;same one's complement hack
  250.  
  251.     ld    a,(0bfffh)
  252.     cp    b
  253.     jp    z,ok_16        ;we have at least 16k of RAM
  254.  
  255.     ld    sp,0a000h
  256.     ld    d,0ffh        ;blink CON LED
  257.     ld    e,0        ;but not STA LED  (i.e., we have 8k)
  258.     jp    stack_loaded    ;else we only have 8k of RAM
  259.                 ;because previous compare failed
  260.  
  261. ;Here if we've got at least 16K RAM
  262. ok_16:
  263.     ld    a,55h        ;one value
  264.     ld    (0bfffh),a
  265.     ld    a,0aah
  266.     ld    (0ffffh),a    ;other value
  267.  
  268.     ld    a,(0bfffh)    ;get what should be 55h if 32k
  269.  
  270.     cp    55h        
  271.     ld    sp,0
  272.     ld    de,0ffffh    ;blink both CON and STA LEDs (if 32k)
  273.     jr    z,stack_loaded    ;if is 55h, then we've got 32 K, else 16 k
  274.  
  275.     ld    sp,0c000h    ;force stack value.
  276.     ld    d,0        ;do not blink CON LED if 16k RAM
  277.  
  278. stack_loaded:
  279.     push    de        ;DE has logical values which tell us which
  280.                 ;LEDs to flash (which we do later...)
  281.     exx
  282.     pop    hl        ;temp. store this info in other reg set
  283.     exx
  284.  
  285.  
  286. ;Clear out RAM.
  287.  
  288.     ld    hl,0
  289.     add    hl,sp        ;now HL has value of SP (That is, Top of
  290.                 ;Memory + 1)
  291.     dec    hl        ;Now HL has Top of Memory address
  292.     ld    de,free_RAM    ;get start of available free RAM
  293.     xor    a        ;clear carry and set A to 0
  294.     ld    (de),a        ;first free RAM location is zeroed....
  295.     sbc    hl,de        ;get into HL # of bytes of free RAM.  If we 
  296.                 ;are in ROM, then all RAM is free, else if we
  297.                 ;are running from RAM, the code part is not
  298.                 ;free, and this compensates for this.
  299.  
  300.     dec    hl        ;one fewer bytes for number to move...
  301.     ld    b,h
  302.     ld    c,l        ;get Byte Count into BC
  303.  
  304.     ld    h,d
  305.     ld    l,e        ;get "source" address = Free_RAM
  306.  
  307.     inc    de        ;set "destination" address = Free_RAM + 1
  308.  
  309.     ldir            ;Zero memory.
  310.  
  311. ;This sequence loads up our data area in RAM:
  312.  
  313.     ld    hl,data_init
  314.     ld    de,nbuffers
  315.     ld    bc,data_size
  316.     ldir
  317.  
  318.  
  319. ; Set stack size and init free buffer list.
  320.  
  321.     ld    hl,0
  322.     add    hl,sp            ;get value of SP, high memory
  323.     ld    de,100            ;50 words for stack
  324.     or    a            ;clear carry
  325.     sbc    hl,de            ;now hl has "pseudo top of memory"
  326.     ld    de,bottom        ;"pseudo bottom of memory"
  327.     or    a
  328.     sbc    hl,de            ;hl now has size of available memory
  329.     rl    l            ;put MSB into carry
  330.     rl    h            ;put carry into LSB
  331.                     ;now h has number of buffers available
  332.     ld    a,h
  333.     ld    (nbuffers),a        ;save this number in memory
  334.  
  335.     ld    hl,bottom        ;beginning of buffer space
  336.     ld    (free),hl        ;now it's also top of free list
  337. ; init buffer free list
  338.     ld    b,a            ;get nbuffers (see above)
  339.     dec    b            ;because last one has 0 as "next"
  340. ibloop:
  341.     push    hl
  342.     ld    de,128
  343.     add    hl,de            ;HL has "next" pointer
  344.     ex    de,hl            ;DE has "next" pointer
  345.     pop    hl            ;HL now has pointer to current buffer
  346.  
  347.     ld    (hl),e            ;low byte of "next" pointer first
  348.     inc    hl
  349.     ld    (hl),d            ;now hi byte
  350.     inc    hl
  351.     xor    a
  352.     ld    (hl),a            ;zero out count field
  353.     inc    hl
  354.     ld    (hl),a            ;zero out # of bytes read field
  355.  
  356.     ex    de,hl            ;HL is now pointer to next buffer
  357.     djnz    ibloop            ;and init all the available buffers
  358.  
  359.     xor    a
  360.     ld    (hl),a            ;Last "next" address is 0
  361.     inc    hl
  362.     ld    (hl),a            ;ditto
  363.  
  364.     inc    hl
  365.     ld    (hl),a            ;zero out count field
  366.     inc    hl
  367.     ld    (hl),a            ;zero out # of bytes read field
  368.  
  369. ;init regs for ib_ext interrupt
  370.     exx
  371.     ld    bc,0            ;set prev state of SYNC pin,for 1200hz
  372.     ld    de,0            ;count of # of interrupts init
  373.     exx
  374.  
  375.     xor    a
  376.     ld    (RX_Allocated_Buffer),a        ;not receiving at this time
  377.  
  378.     ld    hl,TXQ_enables
  379.     ld    b,N_events
  380. E_clear:
  381.     ld    (hl),a            ; Turn off all the enables of all ...
  382.     inc    hl            ; ... possible events.
  383.     djnz    E_clear
  384.  
  385. ;init the routine addresses in our event table
  386.     ld    hl,R_Delay
  387.     ld    (TXQ_Addresses + 2*0),hl
  388.     ld    hl,R_SlotTime
  389.     ld    (TXQ_Addresses + 2*1),hl
  390.     ld    hl,R_Tail
  391.     ld    (TXQ_Addresses + 2*2),hl
  392.  
  393.  
  394.     ld    a,50
  395.     ld    (TXdelay),a        ; TX delay default is 500 ms
  396.      ld    a,64
  397.     ld    (Persistence),a        ; set default value for Persistence
  398.     ld    a,10
  399.     ld    (SlotTime),a        ; and Slot Time defaults to 100 ms
  400.     ld    a,3            ; (should be 11 for 300 baud)
  401.     ld    (TailTime),a        ; Tail Timer default
  402.  
  403.     ld    a,1            ; Init Sync/Hunt bit state
  404.     ld    (S_H_State),a
  405.  
  406. ; Now have the CON and STA LEDs do a "dance".
  407.  
  408.     exx
  409.     push    hl
  410.     exx
  411.     pop    de        ;we saved the logicals telling us which LEDs
  412.                 ;to flash when we figured out the stacksize.
  413.                 ;This is how we know which LEDs to blink.
  414.  
  415.     ld    b,6        ;Do it 6 times (arbitrary as hell, but should
  416.                 ;be an even number so that the LEDs are off at
  417.                 ;the end of this mess...)
  418.     ld    hl,0        ;use HL as downcounter
  419. dance0:
  420.     ld    a,d
  421.     or    a
  422.     call    nz,CON_Flip
  423.     ld    a,e
  424.     or    a
  425.     call    nz,STA_Flip
  426. dance1:
  427.     dec    hl
  428.     ld    a,h
  429.     or    l
  430.     jp    nz,dance1
  431.  
  432.     djnz    dance0        ;do this 6 times  (3 "cycles")
  433.  
  434. ;Previous stuff showed that the download or boot worked properly...
  435.  
  436.  
  437.  
  438. ;We re-initialize the SIO ports so that we flush garbage chars that may have
  439. ;come in while we were diddling the LEDs.  This is necessary because unless we
  440. ;do this, then the A channel (modem) get RX overrun (esp if TNC was listening
  441. ;to noise) and RX overrun is VERY BAD - so bad, in fact, that I turn on both
  442. ;CON and STA and halt, because this situation should NEVER happen in normal
  443. ;use.  I flush the B (tty) channel in case anything was sent to it in mid-
  444. ;stream.
  445.  
  446.  
  447. ;Re-Init SIO.
  448.  
  449.     in    a,(A_ctl)        ;assure we are talking to ch 0
  450.     ld    c,A_ctl
  451.     ld    b,a_size
  452.     ld    hl,a_init
  453.     otir                ;init sync (modem) port
  454.  
  455. ;Re-Init Async port.
  456.  
  457.     in    a,(B_ctl)        ;assure we are talking to ch 0
  458.     ld    c,B_ctl
  459.     ld    b,b_size
  460.     ld    hl,b_init
  461.     otir                ;init async port & interrupt vector
  462.  
  463.  
  464. ; Prepare to load hi bits of interrupt vector
  465.  
  466.     ld    a,I_Register
  467.     ld    i,a            ;set interrupt page for mode 2 ints
  468.     im    2
  469.     ei                ;let 'em rip!
  470.  
  471. ;-----------------------------------------------------------------------------
  472. ; This is the background program.
  473. ; Note that since everything else is interrupt driven, and saves registers,
  474. ; this part of the code can use registers & expect values to stay.
  475.  
  476. Commutator_loop:
  477.     ld    a,(TX_outstanding)    ;if there are no outstanding TX...
  478.     or    a            ;...frames, then we don't have to...
  479.     jp    z,Scan_Check        ;...worry about Transmitter
  480.  
  481. ; If there are frames to transmit, we may have turned on TXdelay, or we may be
  482. ; transmitting a frame so check first.
  483. ; (This bug found late on 30 Sep 86)  The cleanest way to do 
  484. ; this is to check if we are keyed up.  If so, nothing else to do for now
  485. ; here.  This is the "Last Bug!"  Found at 11:59pm EDT on 30 Sep.
  486.  
  487.     ld    a,(A_WR5)
  488.     and    RTS
  489.     jp    nz,Scan_Check        ;if TX keyed up, nothing for us to
  490.                     ;do here!
  491.  
  492. ; else we've noticed that we've got some frame(s) to send.
  493. ; try to keyup TX
  494.  
  495.     ld    a,(Full_Duplex)
  496.     or    a
  497.     jp    nz,Key_Up        ;if Full Duplex, then there is no
  498.                     ;need to worry about all this silly
  499.                     ;slot time and persistence stuff!
  500.  
  501.     ld    a,(TXQE_SlotTime)    ;get SlotTime timer enable
  502.     or    a
  503.     jp    nz,Scan_Check        ;if we're waiting, keep waiting!
  504.  
  505. ;check if Carrier Detect is active
  506.     ld    a,(DCD_State)        ;DCD_State is set in interrupt routine
  507.     or    a
  508.     jp    nz,Scan_Check        ;If carrier active, wait it out
  509.  
  510. ;So, DCD is inactive; do persistence algorithm
  511.     ld    a,r            ;grab the Z-80 refresh register
  512.     add    a,a            ;double;now 0 <= A reg <= 254
  513.     ld    b,a            ;B holds our "random" number
  514.     ld    a,(Persistence)
  515.     sub    b            ;A reg = Persistence - Random #
  516.     jp    c,No_PTT        ;if (P-r) < 0 then no PTT now
  517.                     ; Note that P=255 means ALWAYS key up
  518.  
  519. ;OK, so we've won with the random number generator.  Keyup TX and start the
  520. ;TXdelay timer
  521.  
  522. Key_Up:
  523.     ld    a,(TXdelay)
  524.     ld    h,0
  525.     ld    l,a            ;HL is 16-bit value of TXdelay
  526.     ld    (TXQT_Delay),hl        ;Get timer value into timer slot
  527.     ld    a,1
  528.     ld    (TXQE_Delay),a        ;Enable this event
  529.  
  530.     ld    a,5
  531.     di                ;we need quite time here.
  532.     out    (A_ctl),a        ;;;Ready to write into WR5 of Ch A
  533.     ld    a,(A_WR5)
  534.     or    RTS            ;;;Turn on the PTT bit...
  535.     ld    (A_WR5),a        ;;;...in the memory copy of WR5
  536.     out    (A_ctl),a        ;;; Keyup transmitter
  537.     ei
  538.     jp    Scan_Check        ;That's all we do for now, we await
  539.                     ;TXdelay event
  540.  
  541. No_PTT:        ;since we lost on Random #, wait SlotTime before trying again
  542.     ld    a,(SlotTime)
  543.     ld    h,0
  544.     ld    l,a            ;HL has 16-bit version of SlotTime
  545.     ld    (TXQT_SlotTime),hl    ;Set up the timer value of this event
  546.     ld    a,1
  547.     ld    (TXQE_SlotTime),a    ;and enable this event
  548. ; Note that this code does not have to be interrupt protected because we
  549. ; really don't care if the slot timer is decremented between being loaded
  550. ; and being enabled.
  551.  
  552. Scan_Check:
  553.     ld    hl,TXQ_enables        ; gear up to check timer routines
  554.     ld    ix,TXQ_timers
  555.     ld    iy,TXQ_addresses
  556.     ld    de,2            ;bump ix & iy by twos
  557.     ld    b,N_events        ;Number of possible events
  558. scan_top:
  559.     ld    a,(hl)
  560.     or    a
  561.     jp    z,scan_bottom        ;if not enabled, check next one
  562.  
  563. ;else is enabled.  Timer expired?
  564.     ld    a,(ix+1)
  565.     ld    c,a            ;save MS byte for possible use later
  566.     or    (ix)
  567.     jr    z,scan_fire        ;fire this if we are at 0 count
  568.  
  569.     ld    a,c
  570.     or    a            ; saves us some time doing it this way
  571.     jp    p,scan_bottom        ; or fire if we are negative
  572.  
  573. scan_fire:
  574.     xor    a
  575.     ld    (hl),a            ;disable this event as it fires
  576.     push    hl
  577.     ld    hl,scan_return        ;load up routine return address
  578.     push    hl            ;save as return address on stack
  579.     ld    h,(iy+1)
  580.     ld    l,(iy)            ;get address of routine to "call"
  581.     jp    (hl)            ;"call" this routine
  582.  
  583. scan_return:                ;where all routines return
  584.     pop    hl            ;get original HL back
  585.  
  586. scan_bottom:
  587.     inc    hl            ;increment enable table pointer
  588.     add    ix,de            ;keep timer table pointer in step
  589.     add    iy,de            ;keep routine table pointer in step
  590.     djnz    scan_top        ;look at all entries in tables
  591.  
  592.  
  593. ;Now see if we need to start an output to RS-232 (host) port
  594.     ld    a,(out_started)
  595.     or    a            ;also clears carry (see below) 
  596.     jp    nz,Commutator_loop    ;if output started, nothing to do
  597.  
  598. ; else we should check to see if we need to start an output
  599.     di
  600.     call    CON_off            ;;;
  601.     ld    hl,(out_head_cbuf)    ;;;grab current top of circ buf ptr
  602.     ld    de,(out_tail_cbuf)    ;;;and where the next free buf ptr is
  603.     ei
  604.                     ;interrupt protect the pickup of the
  605.                     ;two pointers 3 Feb 87
  606.     or    a
  607.     sbc    hl,de
  608.     jp    z,Commutator_loop    ;if the same, nothing to do
  609.  
  610. ;else we need to start an output
  611.     di                ;interrupt protect this section,
  612.                     ;although I'm not sure it needs it...
  613.                     ;3 Feb 87
  614.                     ;note: it should already BE done!
  615.     ld    hl,(out_head_cbuf)    ;;;get pointer to next cbuf to output
  616.     ld    e,(hl)
  617.     inc    hl
  618.     ld    d,(hl)            ;;;DE has pointer to buffer chain
  619.     ld    (out_chain_head),de    ;;;set in interrupt routine's place
  620.     ld    a,1
  621.     ld    (out_started),a        ;;;yes, output started
  622.  
  623.     call    CON_on    
  624. cl_0:
  625.     in    a,(B_ctl)        ;;;look at RR0
  626.     and    TBE            ;;;isolate the TBE bit
  627.     jr    z,cl_0            ;;;wait for transmitter to get done
  628.  
  629.     ld    a,FEND
  630.     out    (B_dat),a        ;;;send FEND character (start txing)
  631.     ei
  632.  
  633.     jp    Commutator_loop        ;keep looking for new opportunity
  634.  
  635. ;*****************************************************************************
  636. ; Timer-driven Events
  637. ;*****************************************************************************
  638.  
  639. ;-----------------------------------------------------------------------------
  640. R_Delay:    ; This routine executes when the TX Delay timer expires.
  641.     push    af
  642.     push    bc
  643.     push    de
  644.     push    hl
  645.     di
  646.     call    TXnext_CBuf        ;gets HL to point to buffer chain, and
  647.                     ;sets TX_Chain_Head for the interrupt
  648.                     ;routine
  649.     ld    a,80h
  650.     out    (A_ctl),a        ;;; reset TX CRC
  651.     call    getchar            ;;; getchar needs int. protection
  652.     out    (A_dat),a        ;;; Ship this char to TX modem
  653.     ld    a,1
  654.     ld    (TX_Started),a        ;;; and, yes Virgina, we've started TX
  655.     ld    a,0c0h
  656.     out    (A_ctl),a        ;;; reset TX underrun/EOM latch
  657.     pop    hl
  658.     pop    de
  659.     pop    bc
  660.     pop    af
  661.     ei
  662.     ret
  663.  
  664. ;-----------------------------------------------------------------------------
  665.  
  666. R_SlotTime:    ;when SlotTime event timer expires, come here.
  667.     ret                ; we were just waiting, so nothing
  668.                     ; else to do here (!)
  669.  
  670. ;-----------------------------------------------------------------------------
  671.  
  672. R_Tail:        ;When tail timer times out, turn off the TX
  673.  
  674.     push    af
  675.     ld    a,5            ;ready to write to WR5 of Ch A
  676.     di                ;;;must have atomic use of A_WR5 & SIO
  677.     out    (A_ctl),a        ;;;Next char to A_ctl goes to WR5
  678.     ld    a,(A_WR5)            ;;;grab A_WR5
  679.     and    NOT RTS            ;;;turn off RTS bit there
  680.     ld    (A_WR5),a            ;;;keep memory copy updated
  681.     out    (A_ctl),a        ;;;and turn off TX now
  682.     ei
  683.     pop    af
  684.     ret
  685.  
  686. ;    include    IA.MAC            ;Modem interrupt catchers
  687. ;;;---------------------------------------------------------------------------
  688. ia_tbe:
  689.     push    af
  690.     push    hl
  691.     ld    a,(TX_Started)
  692.     or    a
  693.     jp    z,ia_t2        ;;; previous frame finished
  694.  
  695.     ld    hl,(TX_Chain_Head)
  696.     call    getchar
  697.     ld    (TX_Chain_Head),hl    ;;; must keep this pointer updated
  698.     jr    z,ia_t1        ;;; no more to send
  699.  
  700.     out    (A_dat),a    ;;; else ship this char out
  701. ia_t9:
  702.     pop    hl
  703.     pop    af
  704.     ei
  705.     reti            ;;; just return from these interrupts
  706.  
  707. ia_t1:
  708. ;    halt            ;;;if it gets here, halt
  709.     xor    a
  710.     ld    (TX_Started),a        ;;; TX is NOT started
  711.     ld    hl,TX_Outstanding    ;;; make is so that one fewer frames
  712.                     ;;; NOT "(TX_Outstanding)" (!) 29 Sep
  713.     dec    (hl)            ;;; are outstanding
  714.     ld    a,28h
  715.     out    (A_ctl),a        ;;; reset TX interrupt pending
  716.     jp    ia_t9
  717.  
  718. ;;;previous frame is done, SIO now sending a flag.  More?
  719. ia_t2:
  720.     ld    a,(TX_Outstanding)
  721.     or    a
  722.     jp    nz,ia_t21        ;;;if more to send, go there
  723.  
  724. ;;; else we're done here, clean up.
  725.     ld    a,28h
  726.     out    (A_ctl),a        ;;; Reset TX interrupt pending
  727.  
  728. ;start Tail timer event
  729.     ld    a,(TailTime)        ;;; { bug found 30 Sep.  It was:
  730.     ld    h,0            ;;; "ld hl,(TailTime)"
  731.     ld    l,a            ;;; [ouch!] }
  732.     ld    (TXQT_Tail),hl        ;;; wait for CRC to clear TX
  733.     ld    a,1            ;;; 8.33 ms/char at 1200 bps
  734.     ld    (TXQE_Tail),a        ;;; TailTime value SHOULD be >=2.
  735.     jp    ia_t9
  736.  
  737. ia_t21:            ;start up next frame
  738.     call    TXnext_CBuf        ;;; get the next buffer chain pointer
  739.                     ;;; setup HL and TX_Chain_Head
  740.     ld    a,80h
  741.     out    (A_ctl),a        ;;; reset TX CRC generator
  742.     call    getchar
  743.     out    (A_dat),a        ;;;get 1st char of next frame
  744.     ld    a,1
  745.     ld    (TX_Started),a        ;;; TX started again
  746.     ld    a,0c0h
  747.     out    (A_ctl),a        ;;; reset TX underrun/EOM latch
  748.     jp    ia_t9
  749.  
  750. ;;;---------------------------------------------------------------------------
  751. ;;; Got a character from the SIO RX interrupt, deal with it
  752. ;;; Extensive mods 3 Feb 87 to be in line with what I now know about SIO...
  753.  
  754. ia_rca:
  755.     push    af
  756.     push    hl
  757.  
  758.     ld    a,(RX_Allocated_Buffer)
  759.     or    a
  760.     jp    nz,ia_rc7    ;;; Go there if we are in "receiving" state
  761.  
  762. ;else we are not yet receiving, so allocate buffer & make us "receiving"
  763.  
  764.     call    allocate_buffer    ;;; get a new buffer
  765. ;    jp    z,ia_rc5    ;;; NO ROOM, flush this frame
  766.  
  767. ;;; if got a buffer, insert this character.
  768. ;;; after doing initial buffer setup.
  769.  
  770. ia_rc6:
  771.     ld    (RX_head),hl    ;;; save chain head address (1st buffer)
  772.     ld    (RX_buf),hl    ;;; tuck away addr of our current buffer
  773.     ld    a,TRUE
  774.     ld    (RX_Allocated_Buffer),a    ;;; and mark that we are receiving
  775.  
  776.     xor    a
  777.     call    putchar        ;;; SLIP' frame "type" field here (Always 0)
  778.  
  779. ia_rc7:
  780.     ld    hl,(RX_buf)    ;;; load up address of our current RX buffer
  781.     in    a,(A_dat)    ;;; grab the pending character
  782.     call    putchar        ;;; and stuff in this particular buffer
  783.     ld    (RX_buf),hl    ;;; HL might have changed in putchar()
  784.  
  785. ;;;*** NOTE!  There is a problem here!  If putchar() has no more room, then
  786. ;;; we need to flush all frames so far accumulated & go into RX_flushing
  787. ;;; state !!! 3 Feb 87
  788.  
  789. ia_rc9:
  790.     pop    hl
  791.     pop    af
  792.  
  793.     ei
  794.     reti            ;;; nothing else to do here
  795.  
  796.  
  797. ;;; if no room, flush this frame (sigh)
  798. ;ia_rc5:
  799. ;    ld    a,TRUE
  800. ;    ld    (RX_flushing),a    ;;; we are in the midst of flushing this frame
  801. ia_rc2:
  802. ;    call    STA_on        ;;;ddd Note that we are in flushing state
  803. ;    in    a,(a_dat)
  804. ;    in    a,(a_dat)
  805. ;    in    a,(a_dat)
  806. ;    in    a,(a_dat)    ;;; empty SIO Silo
  807. ;
  808. ;    jp    ia_rc9
  809.  
  810.  
  811. ;;;---------------------------------------------------------------------------
  812. ;;; From out point of view, this interrupt is only interesting because it
  813. ;;; tells us if we're at end of frame.
  814. ia_special:
  815.     push    af
  816.     push    hl        ;;; regs we'll need
  817.  
  818.     ld    a,1
  819.     out    (A_ctl),a    ;;; ready to read RR1
  820.     in    a,(A_ctl)    ;;; OK, grab RR1
  821.  
  822. ;;; First check if RX overrun.  This is VERY BAD, so what can we do?
  823. ;;; Well, we merely treat it as a bad CRC, that is, just flushing the
  824. ;;; frame.  I don't like dropping chars (and it shouldn;t happen very often)
  825. ;;; but at high speeds, it may occur with 2.5 MHz z80s.
  826.  
  827.     bit    5,a        ;;; RX overrun?
  828.     jp    nz,ia_sp8    ;;; If a problem, treat as bad CRC
  829.                 ;;; That is, flush this frame....
  830. ;ia_sp0:
  831.     bit    7,a        ;;; check state of End of Frame bit
  832.     jp    z,ia_sp8    ;;; Else something weird happened - probably
  833.                 ;;; RX overrun. In any case, flush this frame.
  834.                 ;;; error reset & then exit
  835.                 ;;; that is, treat like it was a CRC error
  836.  
  837. ;;; If End of Frame, check CRC bit for valid.
  838. ia_sp1:
  839.     bit    6,a        ;;; Check CRC error bit
  840.     jp    nz,ia_sp8    ;;; If CRC error bit is on, then was CRC error
  841.  
  842. ;;; First ensure that we indeed have a buffer allocated...
  843.     ld    a,(RX_Allocated_Buffer)
  844.     or    a
  845.     jp    z,ia_sp9    ;;; if no buffer allocated, ignore this.
  846.  
  847. ;;; Else this was a good frame, and we should ship it out to host
  848. ;;; Leave the first CRC character at end of buffer chain in the buffer, as
  849. ;;; getchar() will flush it.
  850.  
  851.     ld    hl,(RX_head)
  852.     call    out_queue_insert    ;;; Shove this buffer string onto
  853.                     ;;; output queue
  854.     xor    a
  855.     ld    (RX_Allocated_Buffer),a    ;;; We don't have a buffer allocated
  856.                     ;;; for the next frame...
  857.     jp    ia_sp9
  858.  
  859. ;;; get here if there was a bad CRC
  860. ia_sp8:
  861.     ld    a,(RX_Allocated_Buffer)    ;;; If we don't have any buffers
  862.                     ;;; allocated, then 
  863.     or    a        ;;;8 Feb - SET CONDITION CODES !!!!!!
  864.     jp    z,ia_sp9    ;;; we MUST NOT "release" them !!! 10 Sep 86
  865.                 ;;; if they are not allocated !!!
  866. ia_spf:
  867.     xor    a
  868.     ld    (RX_Allocated_Buffer),a    ;;; not receiving if we have bad CRC
  869.     ld    hl,(RX_head)
  870.     call    free_chain    ;;; free up all buffer(s)
  871.  
  872. ia_sp9:
  873.     ld    a,30h        ;;; error reset
  874.     out    (A_ctl),a
  875.     in    a,(A_dat)    ;;; Avoid spurious RCA interrupt
  876.  
  877.     ld    a,03h           ;;; [JS] select WR3
  878.     out    (A_ctl),a       ;;; [JS]
  879.     ld    a,0D9h          ;;; [JS] enter hunt mode
  880.     out    (A_ctl),a       ;;; [JS]
  881.     ld    a,1             ;;; [JS]
  882.     ld    (S_H_State),a   ;;; [JS] store sync/hunt state
  883.  
  884.     pop    hl
  885.     pop    af
  886.  
  887.     ei
  888.     reti
  889.  
  890. ;;;---------------------------------------------------------------------------
  891. ;;; for ext/status interrupts on Modem, get DCD state into memory, and
  892. ;;; deallocate any spurious buffers (buffer stuff done 30 Sep 86).
  893. ia_ext:
  894.     push    af
  895.     ld    a,10h        ;;; reset ext/status interrupts
  896.     out    (A_ctl),a
  897.     in    a,(A_ctl)    ;;; grab RR0
  898.         push    af              ;;; [JS] put it aside
  899.         bit     4,a             ;;; [JS] check sync/hunt bit
  900.         jp      nz,ia_ex1       ;;; [JS] no need to worry, if not zero
  901.         ld      a,(S_H_State)   ;;; [JS] it is 0! Did it change?
  902.         or      a               ;;; [JS]
  903.         jp      z,ia_ex9        ;;; [JS] no, this is a DCD- or EOM-interrupt
  904.         ld      a,0             ;;; [JS] indeed, it changed!
  905.         ld      (S_H_State),a   ;;; [JS] next time, we'll know
  906.  
  907.     ld    a,(RX_Allocated_Buffer)    ;;; if we are not in the
  908.                     ;;; receiving state...
  909.     or    a        ;;; then there are no allocated buffers and...
  910.     jp    z,ia_ex9    ;;; we MUST NOT "release" them !!! 10 Sep 86
  911.                 ;;; if no buffers allocated !!!
  912.     xor    a
  913.     ld    (RX_Allocated_Buffer),a    ;;; not receiving
  914.     push    hl
  915.     ld    hl,(RX_head)
  916.     call    free_chain    ;;; free up all buffer(s)
  917.     pop    hl
  918.     jp    ia_ex9
  919. ia_ex1:
  920.     ld    a,1        ;;; [JS] Prepare for next frame start
  921.     ld    (S_H_State),a    ;;; [JS]
  922. ia_ex9:
  923.     pop     af              ;;; [JS] get pushed value of RR0
  924.         and     DCD
  925.         ld      (DCD_State),a   ;;;save for TX keyup DCD detect.  Is 0 if DCD
  926.                                 ;;;is not active, or non-zero if it is active.
  927.         pop     af
  928.         ei
  929.         reti
  930.  
  931.  
  932. ;    include    IB.MAC            ;TTY interrupt catchers
  933.  
  934. ;;;---------------------------------------------------------------------------
  935. ;;; we get here whenever -cts, -dcd or -sync inputs change, as well as break
  936. ;;; detection. Since -dcd
  937. ;;; is always tied to +5 volts, we need only worry about -cts and -sync.
  938. ;;; -cts is wired to pin 20, DTR, of the RS232 connector, and is supposed to
  939. ;;; be used for host to TNC handshaking; we ignore this transition (We assume
  940. ;;; that the host is always ready).  We also ignore break detection.  We are
  941. ;;; only interested in -sync transitions, so we can keep time.
  942. ;;; NOTE!  This is the ONLY routine that is allowed to use the other reg set!!
  943. ;;; deal with break detection...
  944.  
  945. sync_hunt    equ    10h
  946. ib_ext:
  947.     ex    af,af'
  948.     exx            ;;; we want the other registers
  949.     ld    a,10h
  950.     out    (B_ctl),a    ;;; reset ext/status interrupts
  951.     in    a,(B_ctl)    ;;; grab RR0
  952.     ld    d,a        ;;; Hold it for a moment...
  953.     and    sync_hunt    ;;; isolate this bit
  954.     jp    z,ib_s0
  955. ;else sync/hunt is a 1
  956.     ld    a,c
  957.     or    a
  958.     jp    z,ib_s1        ;;; go here if state of sync/hunt changed
  959.  
  960.  
  961. ;;; Here if sync/hunt bit did NOT change - maybe something else did....
  962. ib_s9:
  963.     ld    a,d        ;;; retreive RRO from above
  964.     and    Break_Abort    ;;; Check if we are doing a break/abort thing
  965.     jp    z,ib_NBA    ;;; There if No break/abort
  966.  
  967. ;;; Else Break/Abort bit on, note state change...
  968.     ld    a,1
  969.     ld    (in_break),a    ;;; save in mem  (probably can use E reg...)
  970.     in    a,(B_dat)    ;;; clear out any null character from buffer
  971.     jp    ib_BOK        ;;; Break OK for now...
  972.  
  973. ib_NBA:        ;;;if no break/abort, check if we are in break/abort state.
  974.     ld    a,(in_break)
  975.     or    a
  976.     jp    z,ib_BOK    ;;; Nothing going on, Break OK
  977.  
  978. ;;; Else we were in break mode, and this is the tail end of a break.
  979.     xor    a
  980.     ld    (in_break),a
  981.     in    a,(B_dat)    ;;; discard the single extraneous null
  982. ib_BOK:
  983. ib_s99:
  984.     ex    af,af'
  985.     exx
  986.     ei
  987.     reti            ;;; else something else & we don't care
  988. ib_s0:                ;;; sync/hunt is a 0
  989.     ld    a,c
  990.     or    a
  991.     jp    nz,ib_s1a    ;;; go here if sync/hunt changed
  992.     jp    ib_s9        ;;; else not interested, forget it
  993.  
  994. ;get here if state of sync/hunt changed
  995. ib_s1:
  996.     ld    c,1
  997.     jp    ib_s1b
  998. ib_s1a:                ;;; first fix up C for next tick
  999.     ld    c,0
  1000. ib_s1b:
  1001. ;;; Here when we've seen a real "clock tick" & dealt with C reg
  1002.     inc    b
  1003.     ld    a,b
  1004.     cp    12
  1005.     jp    nz,ib_s99        ;;; we act on every 12th clock tick...
  1006.     ld    b,0            ;;; so reload divisor. This give us an
  1007.                     ;;; effective interrupt rate of 100 Hz
  1008.  
  1009. ;;; Decrement all the timers
  1010.  
  1011.     ld    hl,(TXQ_timers+2*0)    ;;; Get first timer value, and ...
  1012.     dec    hl            ;;; ... decrement it as required.
  1013.     ld    (TXQ_timers+2*0),hl
  1014.  
  1015.     ld    hl,(TXQ_timers+2*1)    ;;; Get second timer value, and ...
  1016.     dec    hl            ;;; ... decrement it as required.
  1017.     ld    (TXQ_timers+2*1),hl
  1018.  
  1019.     ld    hl,(TXQ_timers+2*2)    ;;; Get third timer value, and ...
  1020.     dec    hl            ;;; ... decrement it as required.
  1021.     ld    (TXQ_timers+2*2),hl
  1022.  
  1023.     jp    ib_s99
  1024.  
  1025.  
  1026. ;;;---------------------------------------------------------------------------
  1027. ib_special:
  1028.     push    af
  1029. ib_sp9:            ;;; Normal exit
  1030.     ld    a,30h        ;;; error reset
  1031.     out    (B_ctl),a
  1032.     pop    af
  1033.     ei
  1034.     reti
  1035.  
  1036. ;;;---------------------------------------------------------------------------
  1037. ;;; The TX has become empty, shove a new character out
  1038. ib_tbe:
  1039.     push    af        ;;; new char will return in A
  1040.     push    hl
  1041.  
  1042.     ld    a,(ib_esc_mode)
  1043.     or    a
  1044.     jp    z,ib_t1        ;;; not escaped, so go here
  1045. ;;; else we are escaped, so send escaped char
  1046.     ld    a,(ib_char)    ;;; char which follows escape
  1047.     or    a
  1048.     jp    z,ib_t2        ;;; special case if at end of frame, clean up
  1049.     out    (B_dat),a
  1050.     xor    a
  1051.     ld    (ib_esc_mode),a    ;;; get out of escaped mode
  1052.     jp    ib_t9        ;;; all for now...
  1053. ib_t1:
  1054.     ld    hl,(out_chain_head) ;;; we are currently on this buffer, as...
  1055.     call    getchar        ;;; getchar() needs to know
  1056.     ld    (out_chain_head),hl ;;; maybe HL changed, so save it in case
  1057.  
  1058.     jp    z,ib_tdone    ;;; if no more chars, deal with this
  1059.     cp    FESC
  1060.     jp    z,ib_t1a    ;;; deal with FESC char in data stream
  1061.     cp    FEND
  1062.     jp    z,ib_t1b    ;;; deal with FEND char in data stream
  1063. ;;; else this char is nothing special, so shove it out
  1064.  
  1065.     out    (B_dat),a    ;;; shove it out
  1066.     jp    ib_t9        ;;; if this is not last char, all for now
  1067.  
  1068. ;;; else this is last char, send FEND
  1069. ib_tdone:
  1070.     ld    a,FEND
  1071.     out    (B_dat),a
  1072.     ld    a,1
  1073.     ld    (ib_esc_mode),a    ;;; set special escaped mode by...
  1074.     xor    a
  1075.     ld    (ib_char),a    ;;;... making escaped char a 0
  1076.     jp    ib_t9        ;;; all till TX Buffer goes empty again.
  1077.  
  1078. ; here if are completely done sending frame
  1079. ib_t2:
  1080.     push    de        ;;; need this for a moment
  1081.     ld    hl,(out_head_cbuf)
  1082.     inc    hl
  1083.     inc    hl        
  1084.     ld    de,out_bottom
  1085.     or    a
  1086.     push    hl
  1087.     sbc    hl,de
  1088.     pop    hl        ;;; this may be the one we want
  1089.     pop    de
  1090.     jp    nz,ib_t2a    ;;; yes it is!
  1091.  
  1092.     ld    hl,out_top    ;;; else, make a circular buffer
  1093. ib_t2a:
  1094.     ld    (out_head_cbuf),hl ;;; we will work on this one next
  1095.     xor    a
  1096.     ld    (out_started),a    ;;; not doing outputs anymore
  1097.     ld    (ib_esc_mode),a    ;;; !!! NOT IN ESCAPED MODE ANYMORE !!!
  1098.  
  1099.     ld    a,28h        ;;; NEEDED for ASYNC
  1100.     out    (B_ctl),a    ;;; reset TX interrupt pending
  1101.  
  1102. ib_t9:
  1103.     pop    hl
  1104.     pop    af
  1105.     ei
  1106.     reti            ;;; now get our butts out of here...
  1107.  
  1108. ;;; here is FESC in data stream
  1109. ib_t1a:
  1110.     out    (B_dat),a    ;;; Ship FESC character to port
  1111.     ld    a,TFESC        ;;; ready what will be next char
  1112. ib_t1z:
  1113.     ld    (ib_char),a    ;;; set char for next time
  1114.     ld    a,1
  1115.     ld    (ib_esc_mode),a    ;;; we are in escaped mode
  1116.     jp    ib_t9        ;;; all for now
  1117.  
  1118. ;;; here is FEND in data stream
  1119. ib_t1b:
  1120.     ld    a,FESC
  1121.     out    (B_dat),a
  1122.     ld    a,TFEND
  1123.     jp    ib_t1z        ;;; rest is same as FESC case
  1124.  
  1125.  
  1126. ;;;---------------------------------------------------------------------------
  1127. ;;; Got a char from the TTY port, deal with it.
  1128.  
  1129. ib_rca:
  1130.     push    af
  1131.  
  1132.     in    a,(B_ctl)    ;;; Read RR0; force reg pointer to be 0
  1133.     ld    a,1
  1134.     out    (B_ctl),a    ;;; ready to read RR1
  1135.     in    a,(B_ctl)    ;;; Grab RR1
  1136.     and    Framing_Error    ;;; Isolate the FE bit
  1137.     jp    z,ib_Rtop    ;;; No Framing Error, so process this char
  1138.  
  1139. ;;; Else we have a Framing Error - Ignore this char & flush this frame...
  1140.     call    STA_off        ;;; Off with the LED!
  1141.     in    a,(B_dat)    ;;; Flush erroneous character
  1142.     xor    a
  1143.     ld    (In_state),a    ;;; Force receiver to look for FEND
  1144.     ld    a,(In_Allocated_Buffer)
  1145.     or    a
  1146.     jp    z,ib_rc9    ;;; If no buffer is allocated, done; Exit.
  1147.  
  1148. ;;; Else we were receiving a data SLIP frame, so flush it.
  1149.     push    hl
  1150.     ld    hl,(In_head)
  1151.     call    free_chain    ;;; Dump these buffers back to free list
  1152.     pop    hl
  1153.     jp    ib_rc9        ;;; And get out of here!
  1154.  
  1155. ib_rTop:
  1156.     ld    a,(In_state)    ;;; get our state machine value
  1157.     or    a
  1158.     jr    z,ib_r0        ;;; in state 0, waiting for FEND
  1159.     cp    1
  1160.     jr    z,ib_r1        ;;; in state 1, saw FEND
  1161.     cp    2
  1162.     jp    z,ib_r2        ;;; in state 2, data to follow
  1163.     cp    3
  1164.     jp    z,ib_r3        ;;; saw FESC, expecting TFESC or TFEND
  1165.     cp    10
  1166.     jp    z,ib_r10    ;;; Expecting TXdelay
  1167.     cp    20
  1168.     jp    z,ib_r20    ;;; Expecting P value
  1169.     cp    30
  1170.     jp    z,ib_r30    ;;; Expecting SlotTime value
  1171.     cp    40
  1172.     jp    z,ib_r40    ;;; Expecting TailTime value
  1173.     cp    50
  1174.     jp    z,ib_r50    ;;; Expecting Full/Half duplex value
  1175.  
  1176. ;else we don't know what happened, ignore it.
  1177. ib_rcjunk:
  1178.     in    a,(B_dat)
  1179.     xor    a
  1180.     ld    (In_State),a    ;;;go into In_State 0, FEND hunt
  1181. ib_rc9:
  1182.     pop    af        ;;; throw it away, we don't need junk
  1183.     ei
  1184.     reti
  1185.  
  1186. ;;; Here if we are hunting for FEND character
  1187. ib_r0:
  1188.     call    STA_off
  1189.  
  1190.     in    a,(B_dat)
  1191.     cp    FEND
  1192.     jp    nz,ib_rc9    ;;; if we didn't see an FEND, keep looking
  1193.  
  1194. ;;; else is an FEND, change state
  1195.     ld    a,1
  1196.     ld    (In_state),a
  1197.     jp    ib_rc9
  1198.  
  1199. ;;; Get here if we've seen FEND character; look for command byte
  1200. ib_r1:
  1201.     call    STA_off
  1202.     in    a,(B_dat)
  1203.     cp    FEND
  1204.     jp    z,ib_rc9    ;;; Just another FEND, keep looking for cmd
  1205.  
  1206.     call    STA_on        ;;;getting valid SLIP; show in STA LED
  1207.  
  1208. ;;; Here if we DO NOT have an FEND (expecting command byte)
  1209.     or    a
  1210.     jp    z,ib_r1a    ;;; 0 command means data will follow
  1211.     cp    1
  1212.     jp    z,ib_r1b    ;;; 1 command means TXdelay will follow
  1213.     cp    2
  1214.     jp    z,ib_r1c    ;;; 2 command means P(Persistence) will follow
  1215.     cp    3
  1216.     jp    z,ib_r1d    ;;; 3 command means Slot Time will follow
  1217.     cp    4
  1218.     jp    z,ib_r1e    ;;; 4 command means TailTime to follow
  1219.     cp    5
  1220.     jp    z,ib_r1f    ;;; 5 command means Full/Half duplex to come
  1221.  
  1222. ;;; Here if we receive bogus command byte, flush rest of frame
  1223.  
  1224.     call    STA_off        ;;;bogosity, so turn off STA LED
  1225.  
  1226.     xor    a
  1227.     ld    (In_state),a    ;;; go to state which looks for FEND
  1228.     jp    ib_rc9
  1229.  
  1230. ;;; Data are expected, change state
  1231. ib_r1a:
  1232.     ld    a,2
  1233.     ld    (In_state),a
  1234.     jp    ib_rc9
  1235.  
  1236. ;;; TXdelay to follow, change state
  1237. ib_r1b:
  1238.     ld    a,10
  1239.     ld    (In_state),a
  1240.     jp    ib_rc9
  1241.  
  1242. ;;; P to follow, change state
  1243. ib_r1c:
  1244.     ld    a,20
  1245.     ld    (In_state),a
  1246.     jp    ib_rc9
  1247.  
  1248. ;;; SlotTime to follow, change state
  1249. ib_r1d:
  1250.     ld    a,30
  1251.     ld    (In_state),a
  1252.     jp    ib_rc9
  1253.  
  1254. ;;; TailTime to follow, change state
  1255. ib_r1e:
  1256.     ld    a,40
  1257.     ld    (In_state),a
  1258.     jp    ib_rc9
  1259.  
  1260.  
  1261. ;;; Full/Half Duplex to follow, change state
  1262. ib_r1f:
  1263.     ld    a,50
  1264.     ld    (In_state),a
  1265.     jp    ib_rc9
  1266.  
  1267.  
  1268. ;;; These bytes are data
  1269. ib_r2:
  1270.     in    a,(B_dat)
  1271.     cp    FEND
  1272.     jr    z,ib_r2b    ;;; FEND means to queue this buffer
  1273.     push    af        ;;; Save the char we read on stack for a bit..
  1274.  
  1275.     ld    a,(In_Allocated_Buffer)
  1276.     or    a
  1277.     jp    nz,ib_r2c    ;;; if we already allocated buffer
  1278.  
  1279.     push    hl
  1280.     call    allocate_buffer    ;;; get our initial buffer to mess with
  1281.     jp    nz,ib_r22
  1282.  
  1283. ;;;else no room, flush this frame
  1284.     pop    hl        ;;; keep stack tidy
  1285.     xor    a
  1286.     ld    (In_State),a
  1287.     jp    ib_rc9
  1288.  
  1289. ib_r22:
  1290.     ld    a,1
  1291.     ld    (In_Allocated_Buffer),a    ;;; make ourselves active
  1292.  
  1293.     ld    (In_buffer),hl
  1294.     ld    (In_head),hl    ;;; save current & head of chain pointers
  1295.     pop    hl
  1296.  
  1297. ib_r2c:
  1298.     pop    af        ;;; Retreive the data char we just got...
  1299.     cp    FESC
  1300.     jr    z,ib_r2a    ;;; If FESC in data stream, switch state
  1301.  
  1302.     push    hl
  1303.     ld    hl,(In_buffer)
  1304.     call    putchar        ;;; shove this character into our buffer
  1305.     ld    (In_buffer),hl    ;;; save in case HL changed
  1306.     pop    hl
  1307.     jp    ib_rc9        ;;; done so far
  1308.  
  1309. ;;; FESC character seen while grabbing data
  1310. ib_r2a:
  1311.     ld    a,3
  1312.     ld    (In_state),a    ;;; go to this other state
  1313.     jp    ib_rc9
  1314.  
  1315. ;;; FEND character seen while grabbing data
  1316. ib_r2b:
  1317.     ld    a,(In_Allocated_Buffer)
  1318.     or    a
  1319.     jr    z,ib_r2z    ;;; No bytes accumulated, so is null frame
  1320.  
  1321. ;;; else we must ship this frame to TX
  1322.     push    hl        ;;; This bug found 29 Sep (must save HL !!!)
  1323.     ld    hl,(In_Buffer)
  1324.     call    putchar        ;;; put a garbage character at the end of
  1325.                 ;;; last buffer because getchar() will strip
  1326.                 ;;; it. Hack needed because of RX use of
  1327.                 ;;; putchar/getchar.
  1328.      ld    hl,(In_head)
  1329.     call    TX_queue_insert
  1330.     pop    hl
  1331.     xor    a
  1332.     ld    (In_Allocated_Buffer),a    ;;; input no longer active
  1333. ib_r2z:                ;;; entry point for null frame
  1334.     ld    a,1        ;;; Keep as was, FENDs only at end in v.32
  1335.     ld    (In_state),a    ;;; go look for another frame
  1336.  
  1337.     call    STA_off        ;;;done getting this frame, turn STA LED off
  1338.  
  1339.     jp    ib_rc9
  1340.  
  1341.  
  1342. ;;; here if we've seen FESC in data stream
  1343. ib_r3:
  1344.     in    a,(B_dat)
  1345.     cp    TFESC
  1346.     jr    z,ib_r3a
  1347.     cp    TFEND
  1348.     jr    z,ib_r3b
  1349.  
  1350. ;;; Else we don't know what the hell it is, so ignore & keep collecting bytes
  1351.     ld    a,2
  1352.     ld    (In_state),a    ;;; go back into "data receiving" state
  1353.     jp    ib_rc9
  1354.  
  1355. ;;; here if we've seen TFESC after an FESC in data stream; write an FESC
  1356. ib_r3a:
  1357.     ld    a,FESC
  1358. ib_r3z:
  1359.     push    hl
  1360.     ld    hl,(In_buffer)
  1361.     call    putchar
  1362.     ld    (In_buffer),hl
  1363.     pop    hl
  1364.     ld    a,2
  1365.     ld    (In_state),a        ;;; get out of escaped mode
  1366.     jp    ib_rc9
  1367.  
  1368. ;;; Here if we've seen TFEND after FESC in data stream; write FEND
  1369. ib_r3b:
  1370.     ld    a,FEND
  1371.     jp    ib_r3z            ;;; rest is same as for TFESC case
  1372.  
  1373.  
  1374. ;;; This character is interpreted as TXdelay
  1375. ib_r10:
  1376.     in    a,(B_dat)
  1377.     ld    (TXdelay),a
  1378.     xor    a    
  1379.     ld    (In_state),a    ;;; go back to FEND hunt state
  1380.     jp    ib_rc9
  1381.  
  1382. ;;; This charcter is P, Persistence value
  1383. ib_r20:
  1384.     in    a,(B_dat)
  1385.     ld    (Persistence),a
  1386.     xor    a
  1387.     ld    (In_state),a    ;;; go back to FEND hunt state
  1388.     jp    ib_rc9
  1389.  
  1390. ;;; This character is SlotTime value
  1391. ib_r30:
  1392.     in    a,(B_dat)
  1393.     ld    (SlotTime),a
  1394.     xor    a
  1395.     ld    (In_state),a    ;;; go back to FEND hunt state
  1396.     jp    ib_rc9
  1397.  
  1398.  
  1399. ;;; This character is TailTime value
  1400. ib_r40:
  1401.     in    a,(B_dat)
  1402.     ld    (TailTime),a
  1403.     xor    a
  1404.     ld    (In_state),a    ;;; go back to FEND hunt state
  1405.     jp    ib_rc9
  1406.  
  1407.  
  1408. ;;; This character is Full/Half Duplex value
  1409. ;;; 0 means Half Duplex, non-zero means Full Duplex
  1410. ib_r50:
  1411.     in    a,(B_dat)
  1412.     ld    (Full_Duplex),a
  1413.     xor    a
  1414.     ld    (In_state),a    ;;; go back to FEND hunt state
  1415.     jp    ib_rc9
  1416.  
  1417. ;    include    BUFFERS.MAC        ;all buffer-related stuff in here
  1418.                     ;plus all (eventually) variables
  1419. ;
  1420. ; The buffer list is kept from "bottom" to the end of RAM.  The format of the
  1421. ; buffers is:
  1422.  
  1423. ;+------+--------+-------+---------------------------------------------------+
  1424. ;| next | Nbytes | Nread | data                             |
  1425. ;+------+--------+-------+---------------------------------------------------+
  1426. ;
  1427. ; 2 bytes 1 byte   1 byte  124 bytes  (Total 128 bytes)
  1428.  
  1429. ; next     Pointer to next buffer on this buffer chain (or 0 if no more)
  1430. ; Nbytes Number of bytes in this buffer that are valid
  1431. ; Nread  Number of bytes read from this buffer (used by getchar)
  1432. ; data   124 bytes of data (not all is necessarily valid, see Nbytes field)
  1433. ;
  1434. ; The buffer pool is all here, and as processes need buffer space, it is all
  1435. ; allocated out of this pool.  See allocate_buffer and free_buffer code.
  1436.  
  1437.  
  1438. ;;;---------------------------------------------------------------------------
  1439. ;;; return in HL a pointer to a free buffer.  If there are not more buffers,
  1440. ;;; return with Z flag set.
  1441. ;;; destroys no registers except return value HL.
  1442. ;;; IS CALLED FROM AN INTERRUPT ROUTINE, so this operation is atomic.
  1443.  
  1444. allocate_buffer:
  1445.  
  1446.     push    bc
  1447.     push    af
  1448.  
  1449.     ld    hl,(free)        ;;;get pointer to head of free list
  1450.     ld    a,h
  1451.     or    l
  1452.     jp    nz,OK_allocate_buffer    ;;; assure we're not off the end
  1453.  
  1454. ;get here if no more buffers.  Return Z set - do not disturb A.
  1455.     pop    af
  1456.     ld    b,a            ;;; tuck A away for a moment...
  1457.     xor    a            ;;; turn on Z bit
  1458.     ld    a,b            ;;; retreive original A
  1459.     pop    bc
  1460.     ret
  1461.  
  1462. OK_allocate_buffer:
  1463.  
  1464.     xor    a
  1465.     ld    c,(hl)            ;;;grab lo byte of next free buffer
  1466.     ld    (hl),a            ;;; clear it out
  1467.     inc    hl
  1468.     ld    b,(hl)            ;;; "ld bc,(hl)" now hi byte
  1469.     ld    (hl),a            ;;; clear it out, too
  1470.     ld    (free),bc        ;;; update with new free list pointer
  1471.  
  1472.     dec    hl            ;;; Now HL is at head of new buffer
  1473.  
  1474.     pop    af
  1475.     ld    b,a            ;;; tuck A away for a moment...
  1476.     ld    a,1
  1477.     or    a            ;;; Turn Z bit off (i.e., all OK)
  1478.     ld    a,b            ;;; retreive original A
  1479.  
  1480.     pop    bc
  1481.     ret
  1482.  
  1483. ;;;---------------------------------------------------------------------------
  1484. ;;; free_buffer gets passed a pointer (in HL) to a buffer to be freed.  The
  1485. ;;; buffer is placed on the head of the free list.  The nbytes & nread fields
  1486. ;;; are made 0 before placing on free list.
  1487. ;;; THIS ROUTINE IS CALLED AT INTERRUPT LEVEL, so results are atomic.
  1488. ;;; no registers are disturbed at all.  The FREE pointer is updated, however.
  1489. ;;; 159 T states [ 63.6 usec @ 2.5 MHz ]
  1490.  
  1491. free_buffer:
  1492.     push    af
  1493.     push    bc        ;;;we'll use these
  1494.     push    hl        ;;;this will be new head of free list
  1495.  
  1496.     ld    bc,(free)    ;;;get old free head
  1497.     ld    (hl),c        ;;;put on free chain, first low byte...
  1498.     inc    hl
  1499.     ld    (hl),b        ;;; ...now hi byte
  1500.     xor    a
  1501.     inc    hl
  1502.     ld    (hl),a        ;;; zero out nbytes field
  1503.     inc    hl
  1504.     ld    (hl),a        ;;; and the nread field of new head of free
  1505.  
  1506.     pop    hl        ;;;get new head of free list back
  1507.     ld    (free),hl    ;;;and save it in memory where it belongs
  1508.  
  1509.     pop    bc
  1510.     pop    af
  1511.     ret
  1512. ;;; --------------------------------------------------------------------------
  1513. ;;; putchar - HL contains pointer to buffer, A contains the character to put
  1514. ;;; into the buffer.  Upon return, char is put into this buffer if ther is
  1515. ;;; room, else another buffer is allocated and HL is updated to point to this
  1516. ;;; new buffer.  The new buffer is chained onto the old buffer in this case.
  1517. ;;; The calling routine is responsible for maintaing both the head of a
  1518. ;;; particular buffer chain (if it needs it), and the current buffer being
  1519. ;;; manipulated. THIS ROUTINE IS CALLED AT INTERRUPT LEVEL, so is atomic.  No
  1520. ;;; registers disturbed, except that HL may have a new value.
  1521. ;;; 211 T states [  84.4 usec @ 2.5 MHz ]    no new buffer required
  1522. ;;; 338 T states [ 135.2 usec @ 2.5 MHz ]    New buffer needed
  1523.  
  1524. putchar:
  1525.     push    bc
  1526.     push    ix
  1527.     push    af
  1528.     push    hl        ;;;do it this way for a reason...
  1529.  
  1530.     pop    ix        ;;;get buffer pointer into IX
  1531.     ld    a,(ix+2)    ;;;grab nbytes field
  1532.     cp    124        ;;;max of 124 chars in a buffer
  1533.     call    z,putc_need_new_buffer
  1534. ;;; if it takes this call, it returns with a new buffer, with HL pointing to
  1535. ;;; it (as well as IX), and with A reg set to 0.
  1536. ;;; else just plunk into buffer
  1537.     inc    (ix+2)        ;;;one more char will go into this buffer
  1538.     ld    c,a        ;;;get previous nbytes
  1539.     xor    a
  1540.     ld    b,a        ;;; bc <- nbytes, filled out to 16 bits
  1541.     add    ix,bc        ;;; update ix to point to where char goes
  1542.     pop    af        ;;; retreive the char we want to save
  1543.     ld    (ix+4),a    ;;; save it in this buffer
  1544.  
  1545.     pop    ix
  1546.     pop    bc
  1547.     ret            ;;;done for the moment
  1548.  
  1549. ;;; 127 T states [ 50.8 usec @ 2.5 MHz ] (really part of prev routine)
  1550. putc_need_new_buffer:        ;;;prev buffer filled, get a new one
  1551.     push    de        ;;; working registers
  1552.     push    hl        ;;; save current buffer pointer
  1553.     call    allocate_buffer    ;;; grab a new buffer, addr is in HL
  1554.     ex    de,hl        ;;; "ld de,hl" - get new addr into DE for now
  1555.     pop    hl
  1556.     ld    (hl),e        ;;; link new buffer onto chain, lo byte first
  1557.     inc    hl
  1558.     ld    (hl),d        ;;; now hi byte, chaining done
  1559.  
  1560.     ex    de,hl        ;;; update HL for orig. calling routine's use
  1561.     push    hl
  1562.     pop    ix        ;;; upper routine needs ix pointing to new buf
  1563.     xor    a        ;;; and A is nbytes in calling routine, make..
  1564.                 ;;; zero for a new buffer
  1565.     pop    de        ;;; done with this working register
  1566.     ret            ;;; all done here, let calling routine finish
  1567.  
  1568. ;;; --------------------------------------------------------------------------
  1569. ;;; getchar - grab a character from the buffer pointed at by HL, return in A.
  1570. ;;; if the "nread" field of this buf = "nbytes" then this buffer is exhausted,
  1571. ;;; so follow the chain on to the next buffer & release old buffer.  If the
  1572. ;;; next chain is 0, or if the nbytes field is >= nread field, then there are
  1573. ;;; no more bytes.  In this case, return with Z bit set; normally return with
  1574. ;;; Z bit reset (That is, non-zero) indicating a valid char is in A.  Note
  1575. ;;; that if we need to follow the chain to a new buffer, HL will be updated,
  1576. ;;; too, so that the calling routine needs to deal with this.
  1577. ;;;         no registers changed except AF and possibly HL.
  1578. ;;; CALLED AT INTERRUPT LEVEL, so operation is atomic.
  1579. ;;; 212 T states [  84.8 usec @ 2.5 MHz ]    No new buffer needed
  1580. ;;; 493 T states [ 197.2 usec @ 2.5 MHz ]    if following chain
  1581.  
  1582. getchar:
  1583.     push    ix        ;;; save because is working reg
  1584.     push    bc        ;;; working regs here
  1585.  
  1586.     push    hl
  1587.     pop    ix        ;;; ix points to this buffer
  1588.  
  1589.     ld    a,(ix+3)    ;;; grab Nread
  1590.     cp    (ix+2)        ;;; compare with Nbytes
  1591.     call    z,getc_new_buf    ;;; if they are same, this buffer is spent
  1592.  
  1593.     inc    (ix+3)        ;;; we are reading one more char, update Nread
  1594.     inc    a
  1595.     cp    (ix+2)
  1596.     jp    nz,getc_pluck_character    ;;; if not looking at last character
  1597.  
  1598. ;;; else, is the "next" pointer 0?
  1599.     push    hl
  1600.     ld    b,a        ;;; !!!!! SAVE  A   REG  !!!!!!! 4 Jan 87
  1601.     ld    a,(hl)
  1602.     inc    hl
  1603.     or    (hl)
  1604.     ld    a,b        ;;; !!!! Restore A Reg  (Gasp!)
  1605.     pop    hl
  1606.     jr    nz,getc_pluck_character
  1607.  
  1608. ;;; else next is 0 and we are on last char - flush it & quit
  1609.     call    free_buffer
  1610.     pop    bc
  1611.     pop    ix
  1612.     ret            ;;; note that Z bit is set (from above)
  1613.  
  1614. ;;; else we can just pluck a character out of this buffer
  1615. getc_pluck_character:
  1616.     dec    a        ;;; fix A from above mucking around...
  1617.  
  1618.     ld    c,a        ;;; get old Nread into BC
  1619.     ld    b,0        ;;; ditto
  1620.     add    ix,bc        ;;; fix buffer pointer
  1621.     ld    a,1
  1622.     or    a        ;;; make Z bit reset
  1623.     ld    a,(ix+4)    ;;; get the desired byte
  1624.  
  1625.     pop    bc
  1626.     pop    ix
  1627.     ret            ;;; all for this simple case
  1628.  
  1629. ;;; old buffer is spent, get new one (if any)
  1630.  
  1631. getc_new_buf:
  1632.     push    de        ;;; need this reg here
  1633.     ld    e,(hl)        ;;; get lo byte of Next pointer
  1634.     inc    hl
  1635.     ld    d,(hl)        ;;; hi byte of Next pointer (now all in DE)
  1636.     dec    hl        ;;; HL now back to point at spent buffer
  1637.     call    free_buffer    ;;; give the buffer back
  1638.  
  1639.     ex    de,hl        ;;; "ld hl,de" - follow chain
  1640.     push    hl
  1641.     pop    ix        ;;; init new IX (same as HL in this routine)
  1642.     xor    a        ;;; A holds Nread (needed above)
  1643.     pop    de        ;;; release DE from use by this excursion
  1644.     ret
  1645.  
  1646. ;;; --------------------------------------------------------------------------
  1647. ;;; free_chain - MUST be called from interrupt routine to guarantee
  1648. ;;; atomicity.  Takes buffer chain pointed at by HL and returns them to free
  1649. ;;; buffer list
  1650. ;;; 303 T states + (n_on_chain-1)*238 T states
  1651. ;;; [ 121.2 usec + (n_on_chain-1)*95.2 usec ]
  1652.  
  1653. free_chain:
  1654.     push    af
  1655.     push    de
  1656.     push    hl        ;;; we will muck with these
  1657.  
  1658. fc_0:
  1659.     ld    e,(hl)        ;;; get lo part of next buffer pointer
  1660.     inc    hl
  1661.     ld    d,(hl)        ;;; now hi part of next buffer pointer
  1662.     dec    hl
  1663.     call    free_buffer    ;;; release this buffer
  1664.     ld    a,d
  1665.     or    e
  1666.     jp    z,fc_9        ;;; if "next" address is 0, we are at end
  1667. ;;; else we've got more on this chain - deal with them.
  1668.     ex    de,hl        ;;; "ld hl,de" - HL points to "next"
  1669.     jp    fc_0
  1670.  
  1671. fc_9:
  1672.     pop    hl
  1673.     pop    de
  1674.     pop    af
  1675.     ret
  1676.  
  1677. ;;; --------------------------------------------------------------------------
  1678. ;;; out_queue_insert - Places the just-received buffer on the output queue.
  1679. ;;; The address of the RX buffer just received is in HL.
  1680. ;;; The output queue is a circular buffer.  The output routine keeps sending
  1681. ;;; out buffers until its out_head_cbuf pointer equals its out_tail_cbuf
  1682. ;;; pointer. The output routine never mucks with the out_tail_cbuf pointer;
  1683. ;;; similarly, this routine never changes the out_head_cbuf pointer.  So it
  1684. ;;; is possible to
  1685. ;;; insert new entries into the output circular buffer queue without
  1686. ;;; disturbing the entry which is being sent to the output port.
  1687.  
  1688. out_queue_insert:
  1689.     push    af
  1690.     push    de
  1691.     push    hl        ;;; use these
  1692.  
  1693.     ex    de,hl        ;;; "ld de,hl" - move buffer to link addr
  1694.     ld    hl,(out_tail_cbuf) ;;; Grab next free location 
  1695.     ld    (hl),e        ;;; set lo addr 1st
  1696.     inc    hl
  1697.     ld    (hl),d        ;;; now hi addr
  1698.     inc    hl        ;;; Now HL points to next free entry in...
  1699.     ld    de,out_bottom    ;;; ...circ buf, unless we're at end
  1700.     or    a        ;;; clear carry
  1701.     push    hl        ;;; (may be be needed address)
  1702.     sbc    hl,de
  1703.     pop    hl        ;;; get back what we think is good
  1704.     jp    nz,oqi_0
  1705.     
  1706.     ld    hl,out_top    ;;; get here if we're at end of circ buffer.
  1707. oqi_0:
  1708.     ld    (out_tail_cbuf),hl
  1709.     pop    hl
  1710.     pop    de
  1711.     pop    af        ;;; keep clean
  1712.     ret
  1713.  
  1714.  
  1715. ;;;---------------------------------------------------------------------------
  1716. ;;; TX_Queue_Insert - similar to Out_queue_insert, but with different queue.
  1717. ;;; Also, increments the byte TX_Outstanding (which counts the number of
  1718. ;;; frames ready to be dumped to the modem port).  This routine, like
  1719. ;;; out_queue_insert, does not need to worry about queue wrap-around because
  1720. ;;; there are more entries in each of these queues than there are buffers
  1721. ;;; available.  Yes, I know this is a hack, and wastes some RAM space, but it
  1722. ;;; means I don't have to check for overflows here.
  1723. ;;; The queue is circular, and sometimes I call it a "CBuf" - Circular Buffer
  1724.  
  1725. TX_Queue_Insert:
  1726.     push    af
  1727.     push    de
  1728.     push    hl
  1729.     ex    de,hl            ;;; "ld de,hl" - save chain head in DE
  1730.     ld    hl,(TX_Tail_CBuf)    ;;; Next free location in TX CBuf
  1731.     ld    (hl),e
  1732.     inc    hl
  1733.     ld    (hl),d            ;;; put this chain into TX Queue
  1734.     inc    hl            ;;; HL is next availble TX Queue ...
  1735.     ld    de,TX_Bottom        ;;; ... unless we are at bottom of ...
  1736.     or    a            ;;; ... the TX Queue
  1737.     push    hl
  1738.     sbc    hl,de
  1739.     pop    hl
  1740.     jp    nz,TQI_0        ;;; go there if not at buffer bottom
  1741.  
  1742.     ld    hl,TX_Top        ;;; else reload with top of queue val
  1743. TQI_0:
  1744.     ld    (TX_Tail_CBuf),hl    ;;; save next free queue slot
  1745.     ld    hl,TX_Outstanding
  1746.     inc    (hl)            ;;; +1 more frame outstanding now
  1747.     pop    hl
  1748.     pop    de
  1749.     pop    af
  1750.     ret
  1751.  
  1752. ;-----------------------------------------------------------------------------
  1753. ; Setup HL & TX_Chain_Head for transmission of next chain.
  1754.  
  1755. TXnext_CBuf:
  1756.     push    af
  1757.     push    de
  1758.     ld    hl,(TX_Head_CBuf)
  1759.     ld    e,(hl)
  1760.     inc    hl
  1761.     ld    d,(hl)            ; DE -> next chain to transmit
  1762.     inc    hl            ; HL MIGHT be next CBuf entry pointer
  1763.     push    de
  1764.     ld    de,TX_Bottom
  1765.     or    a            ;clear carry
  1766.     push    hl            ;save what might be correct value
  1767.     sbc    hl,de
  1768.     pop    hl
  1769.     pop    de
  1770.     jp    nz,TXn_1        ;go there if not at end of circ. buf
  1771.  
  1772.     ld    hl,TX_Top        ;else we wrap aroune
  1773. TXn_1:
  1774.     ld    (TX_Head_CBuf),hl    ;save next circ buf pointer in mem
  1775.     ex    de,hl            ;return ptr to next chain to TX in HL
  1776.     ld    (TX_Chain_Head),hl    ;TX RCA routine needs this
  1777.     pop    de
  1778.     pop    af
  1779.     ret
  1780.  
  1781.  
  1782. ;-----------------------------------------------------------------------------
  1783. STA_on:        ;Turn the STA LED on.  ASSUMES that interrupts are disabled!
  1784.     push    af
  1785.     ld    a,5
  1786.     out    (A_ctl),a        ;;; ready to write WR5
  1787.     ld    a,(A_WR5)        ;;; get memory copy
  1788.     and    NOT ALED        ;;; set DTR bit to 0 so LED goes on
  1789.     out    (A_ctl),a        ;;; Actually turn on STA LED now...
  1790.     ld    (A_WR5),a        ;;; update memory copy
  1791.     pop    af
  1792.     ret
  1793. ;-----------------------------------------------------------------------------
  1794. STA_off:    ;Turn the STA LED off.  ASSUMES that interrupts are disabled!
  1795.     push    af
  1796.     ld    a,5
  1797.     out    (A_ctl),a        ;;; ready to write WR5
  1798.     ld    a,(A_WR5)        ;;; get memory copy
  1799.     or    ALED            ;;; set DTR bit to 1 so LED goes off
  1800.     out    (A_ctl),a        ;;; Actually turn off STA LED now...
  1801.     ld    (A_WR5),a        ;;; update memory copy
  1802.     pop    af
  1803.     ret
  1804.  
  1805. ;These routines MUST be called with interrupts disabled!
  1806. ;-----------------------------------------------------------------------------
  1807. STA_flip:
  1808.     push    af
  1809.     push    bc
  1810.     in    a,(A_ctl)        ;;;assure we are talking to ch 0
  1811.     ld    a,5
  1812.     out    (A_ctl),a        ;;; ready to write WR5
  1813.     ld    a,(A_WR5)        ;;; get memory copy
  1814.     ld    b,a            ;;; save original for a moment...
  1815.     and    ALED            ;;; Check the STA LED bit
  1816.     ld    a,b            ;;; retreive original
  1817.     jp    z,STA_f0        ;;; bit is a 0, so LED is on, make off
  1818. ;else make it go on (because it is now off)
  1819.     and    NOT ALED        ;;; set DTR bit to 0 so LED goes on
  1820.     jp    STA_f1
  1821. STA_f0:
  1822.     or    ALED            ;;; set DTR bit to 1 so LED goes off
  1823. STA_f1:
  1824.     out    (A_ctl),a        ;;; Actually turn off STA LED now...
  1825.     ld    (A_WR5),a        ;;; update memory copy
  1826.     pop    bc
  1827.     pop    af
  1828.     ret
  1829.     
  1830. ;-----------------------------------------------------------------------------
  1831. CON_on:
  1832.     push    af
  1833.     ld    a,5
  1834.     out    (B_ctl),a
  1835.     ld    a,BLEDon
  1836.     ld    (B_WR5),a        ;;; save in mem for flip routine
  1837.     out    (B_ctl),a
  1838.     pop    af
  1839.     ret
  1840. ;-----------------------------------------------------------------------------
  1841. CON_off:
  1842.     push    af
  1843.     ld    a,5
  1844.     out    (B_ctl),a
  1845.     ld    a,BLEDoff
  1846.     ld    (B_WR5),a        ;;; save in mem for flip routine
  1847.     out    (B_ctl),a
  1848.     pop    af
  1849.     ret
  1850. ;-----------------------------------------------------------------------------
  1851. CON_flip:
  1852.     push    af
  1853.     push    bc
  1854.     in    a,(B_ctl)        ;;;assure we are talking to ch 0
  1855.     ld    a,5
  1856.     out    (B_ctl),a        ;;; ready to write WR5
  1857.     ld    a,(B_WR5)        ;;; get memory copy
  1858.     ld    b,a            ;;; save original for a moment...
  1859.     and    BLED            ;;; Check the CON LED bit
  1860.     ld    a,b            ;;; retreive original
  1861.     jp    z,CON_f0        ;;; bit is a 0, so LED is on, make off
  1862. ;else make it go on (because it is now off)
  1863.     and    NOT BLED        ;;; set DTR bit to 0 so LED goes on
  1864.     jp    CON_f1
  1865. CON_f0:
  1866.     or    BLED            ;;; set DTR bit to 1 so LED goes off
  1867. CON_f1:
  1868.     out    (B_ctl),a        ;;; Actually turn off CON LED now...
  1869.     ld    (B_WR5),a        ;;; update memory copy
  1870.     pop    bc
  1871.     pop    af
  1872.     ret
  1873.     
  1874.  
  1875.  
  1876.     if    ROM
  1877.  
  1878. Free_RAM    equ    8000h
  1879.  
  1880.     else
  1881.  
  1882. Free_RAM    equ    $
  1883.  
  1884.     endif;    ROM
  1885.  
  1886.  
  1887. ;-----------------------------------------------------------------------------
  1888. ; These are the TX real-time routine data structures.  They are used for
  1889. ; timing required with TX control.  There are 3 actions that must be timed:
  1890. ; 1) TXR_delay        TX Delay Timer (for TXDELAY function)
  1891. ; 2) TXR_SlotTime    Part of p-persistence backoff
  1892. ; 3) TXR_tail        Timer to be sending SYNCs before dropping RTS
  1893.  
  1894. ; The data structure can be thought of logically as this:
  1895. ;
  1896. ;    +------------------------+
  1897. ;    | Routine Enabled (byte) | is 0 if not enabled, non zero if enabled
  1898. ;    +------------------------+--------------------------------------+
  1899. ;    | Pointer to routine to execute when timer expires (word)    |
  1900. ;    +---------------------------------------------------------------+
  1901. ;    | 16-bit downcounter timer value, in 10s of milliseconds (word)    |
  1902. ;    +---------------------------------------------------------------+
  1903. ;
  1904. ; The data structure has one entry for each of the 3 timer events.  Physically
  1905. ; it is organized as 3 separate lists, one for each of the enables, one for
  1906. ; each of the routine pointers, and one for each of the timer values.
  1907. ;
  1908. ; An interupt routine, running at 10 millisecond ticks, decrements the values
  1909. ; in each of the downcount timer whether a routine is enabled or not.  When
  1910. ; downcount value goes to 0 (or negative) then the routine "fires".  This
  1911. ; checking for "firing" happens at non-interrupt level in the commutator loop.
  1912. ; With this scheme, the minimum time before firing is 10 milliseconds, and the
  1913. ; maximum time is 327.67 seconds (over 5 minutes).  For example, for a
  1914. ; TXDELAY of 600 milliseconds, the timer would get loaded with decimal 60.
  1915. ;
  1916. ; When a routine fires, it gets marked as "disabled", so you'd need to
  1917. ; explicitly re-enable it if this is required
  1918.  
  1919. ; Note too that a clock could be easily implemented.  If we inserted another
  1920. ; event into our list with a timeout of 100, then every second a routine would
  1921. ; be called. In that routine, we could increment the seconds field (and
  1922. ; possibly minutes, hours, days, years fields) of a Time-of-Day clock.  We
  1923. ; would immediately re-activate this timer to get the next tick, etc.
  1924.  
  1925.  
  1926. TXQ_Enables    equ    Free_RAM
  1927.         ;ds    4        ; 4 bytes for the enables
  1928.  
  1929. TXQ_Addresses    equ    TXQ_Enables+4
  1930.         ;ds    8        ; 4 words for the routine pointers
  1931.  
  1932. TXQ_Timers    equ    TXQ_Addresses+8
  1933.         ;ds    8        ; 4 words for the routine timers
  1934.  
  1935. ; NOTE the last slot in this table is for R_Test routine, which blinks STA LED
  1936. ; IT IS NOT USED NORMALLY, JUST FOR HELPING ME DEBUG THIS!
  1937.  
  1938. ; Some equates to save us from doing contorted things when we want to check if
  1939. ; a routine is enabled in places other than the commutator loop, or for
  1940. ; enabling routines, etc.
  1941.  
  1942. TXQE_Delay    equ    TXQ_Enables+0
  1943. TXQE_SlotTime    equ    TXQ_Enables+1
  1944. TXQE_Tail    equ    TXQ_Enables+2
  1945.  
  1946. ; Same idea, but for the timer values
  1947.  
  1948. TXQT_Delay    equ    TXQ_Timers+0
  1949. TXQT_SlotTime    equ    TXQ_Timers+2
  1950. TXQT_Tail    equ    TXQ_Timers+4
  1951.  
  1952. ; We don't do this for the routine addresses, since they don't change once
  1953. ; they are initialized.
  1954.  
  1955.  
  1956. TXdelay        equ    TXQ_Timers+8
  1957.         ;ds    1        ; Transmitter Delay time value
  1958.  
  1959. Persistence    equ    TXdelay+1
  1960.         ;ds    1        ; Persistence value
  1961.  
  1962. SlotTime    equ    Persistence+1
  1963.         ;ds    1        ; Slot Time value
  1964.  
  1965. TailTime    equ    SlotTime+1
  1966.         ;ds    1        ; TX Tail Time value
  1967.  
  1968.  
  1969. nbuffers    equ    TailTime+1
  1970.         ;db    0        ;up to 255 buffers
  1971.  
  1972. free        equ    nbuffers+1
  1973.         ;dw    0        ;address of 1st buffer on free list
  1974.  
  1975.  
  1976. RX_buf        equ    free+2
  1977.         ;dw    0        ;address of current Receive buffer
  1978.  
  1979. RX_head        equ    RX_buf+2
  1980.         ;dw    0        ;address of 1st RX buffer
  1981.  
  1982. RX_Allocated_Buffer    equ    RX_head+2
  1983.         ;db    0        ;set non-zero if we're in RX state
  1984.  
  1985. RX_Flushing    equ    RX_Allocated_Buffer+1
  1986.         ;db    0        ;is non-0 if we ran out of buffer
  1987.                     ;space and are currently flushing this
  1988.                     ;frame being received.  Used by
  1989.                     ;ia_rca and reset by ia_ext.
  1990.  
  1991.  
  1992. In_Buffer    equ    RX_Flushing+1
  1993.         ;dw    0        ;addr of current Input buffer
  1994.  
  1995. In_Head        equ    In_Buffer+2
  1996.         ;dw    0        ;addr of 1st Input Buffer
  1997.  
  1998. In_Allocated_Buffer    equ    In_Head+2
  1999.         ;db    0        ;is not 0 if we've already alloc'd buf
  2000.  
  2001. In_State    equ    In_Allocated_Buffer+1
  2002.         ;db    1        ;input state machine state
  2003.                     ;assume that we've seen an FEND from
  2004.                     ;(non-existent) "previous" frame. This
  2005.                     ;means that when we are receiving data
  2006.                     ;from user, there need be ONLY the
  2007.                     ;FEND char at the end of a frame, and
  2008.                     ;not at the beginning (although if a
  2009.                     ;FEND is at the beginning, it is 
  2010.                     ;ignored.)
  2011.  
  2012. Out_Started    equ    In_State+1
  2013.         ;db    0        ;Output not started yet (Logical var)
  2014.  
  2015. Out_Head_CBuf    equ    Out_Started+1
  2016.         ;dw    out_top        ;address of buffer to be output rs232
  2017.  
  2018. Out_Tail_CBuf    equ    Out_Head_Cbuf+2
  2019.         ;dw    out_top        ;pointer to next free output buffer
  2020.  
  2021. Out_Chain_Head    equ    Out_Tail_Cbuf+2
  2022.         ;dw    0        ;addr of buffer we are now outputting
  2023.  
  2024.  
  2025. TX_Started    equ    Out_Chain_Head+2
  2026.         ;db    0        ;non-zero if we've begun TXing chars
  2027.  
  2028. TX_Head_CBuf    equ    TX_Started+1
  2029.         ;dw    TX_Top        ;Current active CBuf entry (if active)
  2030.  
  2031. TX_Tail_CBuf    equ    TX_Head_CBuf+2    ; This said "TX_Head_CBuf_2"...sigh
  2032.                     ;type found 2 Mar 87
  2033.  
  2034.         ;dw    TX_Top        ;next free CBuf entry
  2035.  
  2036.  
  2037.  
  2038. TX_Chain_Head    equ    TX_Tail_Cbuf+2
  2039.         ;dw    0        ;holds address of the current buffer
  2040.                     ;chain head that we are transmitting
  2041.  
  2042. TX_Outstanding    equ    TX_Chain_Head+2
  2043.         ;db    0        ;Number of TX CBufs queued up for TX
  2044.  
  2045.  
  2046. DCD_State    equ    TX_Outstanding+1
  2047.         ;db    0        ;is non 0 if DCD LED is on
  2048.  
  2049. S_H_State    equ    DCD_State+1    ;is 1 if we are in Sync/Hunt state
  2050.         ;db    1
  2051.  
  2052. ;these next two are used by the IB_TBE interrupt routine.
  2053. ib_esc_mode    equ    S_H_State+1
  2054.         ;db    0        ; not in escaped mode 
  2055.  
  2056. ib_char        equ    ib_esc_mode+1
  2057.         ;ds    1        ; next char to send if escaped mode
  2058.  
  2059. in_break    equ    ib_char+1    ; non-zero if we are in a break detect
  2060.         ;db    0        ; on the async port
  2061.  
  2062. Full_Duplex    equ    in_break+1
  2063.         ;db    0        ;not initially Full Duplex
  2064.  
  2065. A_WR5        equ    Full_Duplex+1
  2066.         ;db    ALEDoff        ;state of STA LED & RTS (PTT) line,
  2067.                     ;mainly... (For Ch A only [modem] )
  2068.  
  2069. B_WR5        equ    A_WR5+1
  2070.         ;db    BLEDoff
  2071.  
  2072.  
  2073.  
  2074. Out_Top        equ    B_WR5+2        ;"top" of output circular buffer
  2075.                     ; 255 out buffer chains pending, max
  2076. Out_Bottom    equ    Out_Top+2*255    ;"bottom" of output circular buffer
  2077.  
  2078. TX_Top        equ    Out_Bottom+2
  2079. TX_Bottom    equ    TX_Top+2*255
  2080.  
  2081.  
  2082. Bottom        equ    TX_Bottom+2    ;end of all code & predefined data
  2083.  
  2084.  
  2085. ; Notes on nomenclature:
  2086.  
  2087. ;    out = to TTY port; in = from TTY port
  2088. ;    TX = to modem; RX = from modem
  2089. ;
  2090. ;    ;;; means that that code executes without interrupts enabled (except
  2091. ;        for the initialization code)
  2092. ;
  2093. ;
  2094. ; I have been careful with JR/JP use.  I use JP when the jump is likely and
  2095. ; where speed is important.  I use JR when the jump is unlikely so that I can
  2096. ; save a few cycles.  JP always uses 10 cycles whether it jumps or not, but
  2097. ; JR uses either 7 or 12 T states, no jump/jump, respectively.
  2098.  
  2099.  
  2100. ; Buffers kept here at end.
  2101.     end    start
  2102.